<template>
    <div @mousedown.stop="resetChoice">
        <!-- :style="{ cursor: cursor() }" -->
        <svg id="svgContent" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"
            width="100%" height="100%" data-spm-anchor-id="TODO.11007039.0.i6.12b64a9bcbXQmm" @mousedown="svgMouseDown"
            @mousemove="dragIng" @mouseleave="atMouseOut" @mouseup="dragEnd">
            <g :transform="` translate(${svg_left}, ${svg_top}) scale(${svgScale})`">
                <!-- 节点主体 -->
                <g v-for="(item, i) in DataAll.nodes" :key="'_' + i" class="svgEach"
                    :transform="`translate(${item.pos_x}, ${item.pos_y})`"
                    @contextmenu="r_click_nodes($event, DataAll.nodes[i])"
                    @click="click('node', DataAll.nodes[i].id, i)" @dblclick="focusInput(item)"
                    @mouseleave="leave('node')" @mousedown="dragPre($event, i, item)" @mouseup="dragOver($event, item)">
                    <main-body ref="g" :item="item" :i="i" :choice="choice" :currentEvent="currentEvent"
                        @nodesPersonalEvent="nodesPersonalEvent" :width="calcTextWidth(item.name + item.taskType)"
                        @changeNodeName="changeNodeName" @linkEnd="linkEnd" @linkPre="linkPre" />
                </g>
                <Arrow v-if="DataAll.nodes.length > 1" v-for="(each, n) in DataAll.edges" :key="'____' + n"
                    :DataAll="DataAll" @delEdge="delEdge" :each="each" :index="n" ref="arrow" @click="click"
                    @leave="leave('path')" />
                <SimulateArrow v-if="currentEvent === 'dragLink'" :dragLink="dragLink" />
            </g>
            <SimulateSelArea v-if="showSelArea" :simulate_sel_area="simulate_sel_area" :scale="svgScale" />
            <EditArea @editNodeDetails="editNodeDetails" :isEditAreaShow="is_edit_area"
                @nodesPersonalEvent="nodesPersonalEvent" @openTask="openTask" @delNode="delNode"
                @changePort="changePort" @close_click_nodes="close_click_nodes" />

        </svg>
        <Control @sizeInit="sizeInit" @sizeExpend="sizeExpend" @sizeShrink="sizeShrink" @sel_area="sel_area"
            :modelRunningStatus="modelRunningStatus" :currentEvent="currentEvent" :toolbar="toolbar"
            @addNode="addNodeFunction" @readyMove="readyMove" :nodes="choice.paneNode.length"
            @fullScreen="fullScreen" />
    </div>

</template>

<script setup>
import Arrow from "./arrow.vue"
import SimulateArrow from "./simulateArrow.vue"
import EditArea from "./editArea.vue"
import Control from "./control.vue"
import SimulateSelArea from "./simulateSelArea.vue"
import mainBody from './mainBody.vue'
import { calcTextWidth } from "@/utils/tools"
import { useEventListener } from "@usehook"

const props = defineProps({
    DataAll: {
        type: Object,
        default: () => []
    },
    toolbar: {
        type: Array,
        default: () => []
    },
    addNodeFunction: {
        type: Function,
        default: () => {
            //addNode()
        }
    },
    useKeyMap: {
        default: false,
        type: Boolean
    }
})

const showSelArea = $computed(() => {
    return ['sel_area', 'sel_area_ing'].indexOf(currentEvent) !== -1 && simulate_sel_area.left !== 0
        && simulate_sel_area.top !== 0 && simulate_sel_area.width !== 0 && simulate_sel_area.height !== 0
})

const emits = defineEmits(["focusInput", "npc", "updateDAG", "openTask", "editNodeDetails", "personalClick", "fullScreen"])

let svg_scale = $ref(null)
let choice = $ref({
    paneNode: [], // 选取的节点下标组
    index: -1,
    point: -1 // 选取的点数的下标
})
let dragFrame = $ref({
    // 节点模态
    posX: 9999,
    posY: 9999
})
let dragLink = $ref({
    // 连线模态
    fromX: 0,
    fromY: 0,
    toX: 0,
    toY: 0
})
let currentEvent = $ref(null) // 当前执行的方法
let initPos = $ref({
    // 初始化svg dom位置
    left: -1,
    top: -1
})
let is_edit_area = $ref({
    value: false,
    x: -9999,
    y: -9999
})// 是否在编辑节点
let simulate_sel_area = $ref({
    // 框选节点
    left: 10,
    top: 50,
    width: 0,
    height: 0
})
let svg_left = $ref(0)
let svg_top = $ref(0)
let svg_trans_init = $ref({
    x: 0,
    y: 0
})
let svg_trans_pre = $ref({
    x: 0,
    y: 0
})
let canMouseWheelUse = $ref(true)
let modelRunningStatus = $ref(false)
let multipleSelectNodes = $ref({})
let initMultiplePosition = $ref({})
let currentClick = $ref(null)

const cursor = () => {
    if (currentEvent === 'move_graph') {
        return 'grabbing'
    } else if (currentEvent === 'readyMove') {
        return 'grab'
    } else {
        return 'default'
    }
}

const keyMap = (e) => {
    let { key, keyCode } = e

    if (props.useKeyMap) {
        if (key === 'F1' || keyCode === 112) {
            e.preventDefault()
            e.stopPropagation()//关闭浏览器快捷键
            readyMove()
        } else if (key === 'F2' || keyCode === 113) {
            e.preventDefault()
            e.stopPropagation()//关闭浏览器快捷键
            props.addNodeFunction()
        }
    }
}
const readyMove = () => {
    currentEvent = 'readyMove'
}
const leave = () => {
    currentClick = null;
}
const atMouseOut = () => {
    // 鼠标移出
    currentEvent = currentEvent && currentEvent.indexOf('sel_area') !== -1 ? currentEvent : null;
}
/**
 * 事件分发器
 */
let removed = $ref(false)
const dragOver = (e, item) => {
    if (!removed && !e.ctrlKey && choice.paneNode.length > 1) selPaneNode(item.id)
    removed = false
}

const dragPre = (e, i, item) => {
    e.preventDefault();
    e.stopPropagation();
    e.cancelBubble = true
    removed = false
    if (e.ctrlKey) {
        if (choice.paneNode.includes(item.id)) {
            choice.paneNode.splice(choice.paneNode.indexOf(item.id), 1); // 删除元素
        } else {
            choice.paneNode.push(item.id);
        }
    } else if (choice.paneNode.length <= 1) { selPaneNode(item.id) }
    if (e.button !== 0 || e.srcElement.nodeName.toLowerCase() !== "input" || e.ctrlKey) return

    // 准备拖动节点
    multipleSelectNodes = JSON.parse(JSON.stringify(choice));
    if (multipleSelectNodes && multipleSelectNodes.paneNode.length) {
        initMultiplePosition = JSON.parse(JSON.stringify(props.DataAll.nodes))
    }
    setInitRect(); // 工具类 初始化dom坐标
    currentEvent = "dragPane"; // 修正行为
    sessionStorage['offsetX'] = e.offsetX;
    sessionStorage['offsetY'] = e.offsetY;
    choice.index = i; // 当前选择的接点
    setDragFramePosition(e);
}
const dragIng = (e) => {
    // 事件发放器 根据currentEvent来执行系列事件
    switch (currentEvent) {
        case 'dragPane':
            paneDragIng(e);
            removed = true
            break;
        // case 'PaneDraging':
        //    setDragFramePosition(e); // 触发节点拖动
        //    break;
        case 'dragLink':
            setDragLinkPosition(e); // 触发连线拖动
            break;
        case 'sel_area_ing':
            setSelAreaPosition(e); // 触发框选
            break;
        case 'move_graph':
            graphMoveIng(e);
            break;
        default:
            break;
    }
}
const dragEnd = () => {
    // 拖动结束
    if (currentEvent === 'sel_area_ing') {
        getSelNodes(simulate_sel_area);
    } else {
    }
    currentEvent = null;
}
const svgMouseDown = (e) => {
    // svg鼠标按下触发事件分发
    setInitRect();
    if (currentEvent === "sel_area") {
        selAreaStart(e);
    } else if (currentEvent === 'readyMove') {
        currentEvent = "move_graph";
        graphMovePre(e);
    }
}
/**
 * 连线系列事件
 */
const linkPre = (e, i, nth) => {
    // 开始连线
    setInitRect();
    currentEvent = "dragLink";
    choice = Object.assign({}, choice, { index: i, point: nth });
    setDragLinkPosition(e, true);
    e.preventDefault();
    e.stopPropagation();
}

const linkEnd = (i, nth) => {
    // 连线结束 i, 目标点序号 nth 出发点 choice.index 出发点序号 choice.point
    if (currentEvent === "dragLink") {
        let params = {
            src_node_id: props.DataAll.nodes[choice.index].id,
            src_output_idx: choice.point,
            dst_node_id: props.DataAll.nodes[i].id,
            dst_input_idx: nth
        };
        // 限制不能自进自出
        if (props.DataAll.nodes[i].id !== props.DataAll.nodes[choice.index].id) {
            addEdge(params);
        }
    }
    currentEvent = null;
}
/**
 *  svg画板缩放行为
 */
const sizeInit = () => {
    changeSize("init"); // 回归到默认倍数
    svg_left = 0; // 回归到默认位置
    svg_top = 0;
    sessionStorage["svg_left"] = 0;
    sessionStorage["svg_top"] = 0;
}
const sizeExpend = () => {
    changeSize("expend"); // 画板放大0.1
}
const sizeShrink = () => {
    changeSize("shrink"); // 画板缩小0.1
}
const onMouseWheel = (e) => { // 鼠标滚动或mac触摸板可以改变size
    if (!e) return false;
    let multiple = (e.wheelDelta / 10);
    if (canMouseWheelUse && (multiple * multiple) > 1) {
        multiple > 0
            ? sizeExpend()
            : sizeShrink();
        canMouseWheelUse = false;
        setTimeout(() => { // 节流
            canMouseWheelUse = true
        }, 50)
    }
}
const setMouseWheelEvent = () => { // 绑定鼠标滚轮事件
    const addEvent = (obj, xEvent, fn) => {
        if (obj.attachEvent) {
            obj.attachEvent('on' + xEvent, fn);
        } else {
            obj.addEventListener(xEvent, fn, false);
        }
    };

    let oDiv = document.getElementById('svgContent');
    // 当滚轮事件发生时，执行onMouseWheel这个函数
    addEvent(oDiv, 'mousewheel', onMouseWheel);
    addEvent(oDiv, 'DOMMouseScroll', onMouseWheel);
}
/**
 * 节点事件 单选 框选 拖动
 */
const sel_area = () => {
    currentEvent = "sel_area";
    simulate_sel_area = {
        left: 0,
        top: 0,
        width: 0,
        height: 0
    };
}
const paneDragIng = (e) => {
    let offsetX = sessionStorage['offsetX'] || 0;
    let offsetY = sessionStorage['offsetY'] || 0;
    const x =
        (e.x - initPos.left - (sessionStorage["svg_left"] || 0)) /
        svgScale - 30 - offsetX - 5;
    const y =
        (e.y - initPos.top - (sessionStorage["svg_top"] || 0)) /
        svgScale - offsetY - 1;
    let params = {
        model_id: sessionStorage["newGraph"],
        id: props.DataAll.nodes[choice.index].id,
        pos_x: x,
        pos_y: y
    };
    if (multipleMoveNode && multipleSelectNodes.paneNode.length > 1) {
        multipleMoveNode(params)
    } else {
        moveNode(params);
    }
}
const paneDragEnd = (e) => {
    // 节点拖动结束
    multipleSelectNodes = {};
    initMultiplePosition = null;
    dragFrame = { dragFrame: false, posX: 0, posY: 0 };
    const x =
        (e.x - initPos.left - (sessionStorage["svg_left"] || 0)) /
        svgScale -
        90;
    const y =
        (e.y - initPos.top - (sessionStorage["svg_top"] || 0)) /
        svgScale -
        15;
    let params = {
        model_id: sessionStorage["newGraph"],
        id: props.DataAll.nodes[choice.index].id,
        pos_x: x,
        pos_y: y
    };
}
const selPaneNode = (id) => {
    // 单选节点
    choice.paneNode.length = [];
    if (id) {
        choice.paneNode.push(id);
    }
}
const selAreaStart = (e) => {
    // 框选节点开始
    currentEvent = "sel_area_ing";
    const x = (e.x - initPos.left)
    const y = (e.y - initPos.top)
    simulate_sel_area = {
        left: x,
        top: y,
        width: 0,
        height: 0
    };
}
const setSelAreaPosition = (e) => {
    // 框选节点ing
    const x = (e.x - initPos.left);
    const y = (e.y - initPos.top);
    const width = x - simulate_sel_area.left;
    const height = y - simulate_sel_area.top;
    simulate_sel_area.width = width;
    simulate_sel_area.height = height;
}
const getSelNodes = (positions) => {
    // 选取框选的节点
    const { left, top, width, height } = positions;
    choice.paneNode.length = 0;
    props.DataAll.nodes.forEach(item => {
        if (
            item.pos_x * svgScale > (left - parseInt(sessionStorage["svg_left"] || 0)) &&
            item.pos_x * svgScale < (left + width - parseInt(sessionStorage["svg_left"] || 0)) &&
            item.pos_y * svgScale > (top - parseInt(sessionStorage["svg_top"] || 0)) &&
            item.pos_y * svgScale < (top + height - parseInt(sessionStorage["svg_top"] || 0))
        ) {
            // set the select nodes into data.choice
            choice.paneNode.push(item.id);
        }
    });
    simulate_sel_area = {
        // 触发框选结束
        left: 0,
        top: 0,
        width: 0,
        height: 0
    };
}
const focusInput = (el) => { // 双击选中input
    let id = currentClick.params.id;
    currentClick = null;
    // let e = el.path[0];
    emits('focusInput', id, el)
    // const input = el.path[0];
    // if (input) {
    //   input.focus()
    // }
}
/**
 * 画布拖动
 */
const graphMovePre = (e) => {
    const { x, y } = e;
    svg_trans_init = { x, y };
    svg_trans_pre = { x: svg_left, y: svg_top };

}
const graphMoveIng = (e) => {
    const { x, y } = svg_trans_init;
    svg_left = e.x - x + svg_trans_pre.x;
    svg_top = e.y - y + svg_trans_pre.y;
    sessionStorage["svg_left"] = svg_left;
    sessionStorage["svg_top"] = svg_top;
}
/**
 * 模态框类
 */
const setDragFramePosition = (e) => {
    // 节点拖拽模态
    const x = e.x - initPos.left - (sessionStorage["svg_left"] || 0);
    const y = e.y - initPos.top - (sessionStorage["svg_top"] || 0);
    dragFrame = {
        posX: x / svgScale - 90,
        posY: y / svgScale - 15
    };
}
const setDragLinkPosition = (e, init) => {
    // 节点连线模态
    const x =
        (e.x - initPos.left - (sessionStorage["svg_left"] || 0)) /
        svgScale;
    const y =
        (e.y - initPos.top - (sessionStorage["svg_top"] || 0)) /
        svgScale;
    if (init) {
        dragLink = Object.assign({}, dragLink, {
            fromX: x,
            fromY: y
        });
    }
    dragLink = Object.assign({}, dragLink, { toX: x, toY: y });
}
const close_click_nodes = () => {
    // 关闭模态
    is_edit_area = { value: false, x: -9999, y: -9999 };
}
const click = (t, id) => {
    currentClick = { t: t, params: { id: id } };
}

const r_click_nodes = (e, node) => {
    let open_task = node.taskType !== 'virtual';
    let delete_node = true;
    // 节点右键模态
    setInitRect();
    const id = node.id;
    const detail = node.detail || null;
    const rightClickEvent = node.rightClickEvent || null;
    const x = e.x - initPos.left;
    const y = e.y - initPos.top;
    is_edit_area = {
        value: true,
        x,
        y,
        id,
        detail,
        rightClickEvent,
        taskType: node.taskType,
        name: node.name,
        open_task,
        delete_node
    };
    e.stopPropagation();
    e.cancelBubble = true;
    e.preventDefault();
}
/**
 * 工具类
 */
const setInitRect = () => {
    // 矫正svg组件坐标
    let { left, top } = document.getElementById("svgContent").getBoundingClientRect();
    initPos = { left, top };
}

// 校验连线是否生效
const addEdge = (value) => {
    // 增加边
    let _DataAll = props.DataAll;
    let isAddEdge = true
    const v = value.dst_node_id + "" + value.src_node_id
    for (const edge of _DataAll.edges) {
        //禁止死循环
        const e = edge.dst_node_id + "" + edge.src_node_id
        const e2 = edge.src_node_id + "" + edge.dst_node_id
        if (e === v || e2 === v) {
            isAddEdge = false
            break
        }
    }
    if (isAddEdge) {
        _DataAll.edges.push({
            ...value,
            id: _DataAll.edges.length + 10
        });
        emits('updateDAG', _DataAll, 'addEdge')
    }
}
const delEdge = ({ id }) => {
    // 删除边
    let _edges = [];
    let del_edges = { edges: [] };
    props.DataAll.edges.forEach((item, i) => {
        if (item.id !== id) {
            _edges.push(item)
        } else {
            del_edges.edges.push(item)
        }
    });
    props.DataAll.edges = _edges;
    emits('updateDAG', props.DataAll, 'delEdge', del_edges)
}
const moveNode = (params) => {
    // 移动点的位置
    let _DataAll = props.DataAll;
    _DataAll.nodes.forEach((item, i) => {
        if (item.id === params.id) {
            item.pos_x = params.pos_x;
            item.pos_y = params.pos_y
        }
    });
    emits('updateDAG', _DataAll, 'moveNode')
}
const multipleMoveNode = (params) => {
    // 同时移动多个点
    const { id, pos_x, pos_y } = params;
    let x = 0;
    let y = 0;
    let _initMultiplePosition = initMultiplePosition;
    let _DataAll = props.DataAll;
    _initMultiplePosition.map(item => {
        if (item.id === id) {
            x = pos_x - item.pos_x;
            y = pos_y - item.pos_y;
        }
    });
    _initMultiplePosition.forEach(item => {
        if (multipleSelectNodes.paneNode.indexOf(item.id) > -1) {
            item.pos_x += x;
            item.pos_y += y
        }
    });
    _DataAll.nodes = _initMultiplePosition;
    emits('updateDAG', _DataAll, 'moveNode')
}
const addNode = (params) => {
    let _nodes = props.DataAll.nodes;
    let rej = false;
    _nodes.forEach((e) => {
        if (!e.name) {
            rej = true
        }
    });

    if (rej) {
        emits('updateDAG', 'name exists null,operation reject', 'addNode')
    } else {
        let defaultParams = {
            "in_ports": [
                0
            ],
            "name": null,
            "id": (Math.round(Math.random() * 1000000000000) + new Date().getTime()).toString(),
            "out_ports": [0],
            "pos_x": 50,
            "pos_y": 50,
            "iconClassName": 'el-icon-check',
            "type": "Variable",
            "nodeStyle": {
                "border": "1px solid orange"
            },
            "iconStyle": {
                "background": "orange"
            },
            //detail: [{}]
        };

        // 增加节点
        _nodes.push({ ...defaultParams, ...params });

        emits('updateDAG', props.DataAll, 'addNode')
    }
}
const delNode = ({ id }) => {
    // 删除节点
    let _edges = [];
    let _nodes = [];
    let delNodeParams = { nodes: id, edges: [] };
    props.DataAll.edges.forEach(item => {
        if (item.dst_node_id !== id && item.src_node_id !== id) {
            _edges.push(item)
        } else {
            delNodeParams.edges.push(item);
        }
    });
    props.DataAll.nodes.forEach(item => {
        if (item.id !== id) {
            _nodes.push(item)
        }
    });
    props.DataAll.edges = _edges;
    props.DataAll.nodes = _nodes;
    emits('updateDAG', props.DataAll, 'delNode', delNodeParams)
}
const openTask = (taskInfo) => {
    emits("openTask", taskInfo)
}
const fullScreen = () => {
    emits("fullScreen")
}
const changeSize = (action) => {
    // 改变size
    let svgScale = typeof sessionStorage['svgScale'] === 'string' ? Number(sessionStorage['svgScale']) : 1;
    let _width = window.innerWidth;
    let _height = window.innerHeight;
    switch (action) {

        case 'shrink':
            if (svgScale > 0.5) {

                svgScale -= 0.05;
                svg_left = sessionStorage['svg_left'] = svg_left + _width * 0.01;
                svg_top = sessionStorage['svg_top'] = svg_top + _height * 0.01;
            }
            break;
        case 'expend':
            if (svgScale < 1.5) {
                svgScale += 0.05;
                svg_top = sessionStorage['svg_top'] = svg_top - _height * 0.01;
                svg_left = sessionStorage['svg_left'] = svg_left - _width * 0.01;
            }
            break;
        case 'init':
            svgScale = 1;
            break;
        default:
            break
    }

    svg_scale = sessionStorage['svgScale'] = svgScale

}
const changeNodeName = (params) => {
    // 改变节点名称
    props.DataAll.nodes.forEach(item => {
        if (item.id === params.id) {
            item.name = params.name
        }
    });
    emits('updateDAG', props.DataAll, 'changeNodeName')
}
const changePort = (action, id) => {
    props.DataAll.nodes.forEach(item => {
        if (item.id === id) {
            item[action] ? item[action].push(item[action].length) : item[action] = ['0']
        }
    });
    emits('updateDAG', props.DataAll, 'changePort')
}
const editNodeDetails = (value) => {
    // 抛出待编辑内容
    emits('editNodeDetails', value)
}
const nodesPersonalEvent = (eventName, data) => {
    if (eventName === 'dbClickNodeIcon') {
        emits("npc", props.DataAll.nodes[data].name, props.DataAll.nodes[data].taskType, props.DataAll.nodes[data].id)
    }
    emits('personalClick', eventName, data)
}

const svgScale = $computed(() => {
    return parseFloat(svg_scale || !!sessionStorage['svgScale'] ? sessionStorage['svgScale'] : 1)
})

nextTick(() => {
    setMouseWheelEvent()
});

onMounted(() => {
    sessionStorage["svg_left"] = 0;
    sessionStorage["svg_top"] = 0;
})

const resetChoice = (e) => {
    if (e.srcElement.id === 'svgContent' && choice.paneNode.length > 0) {
        choice = {
            paneNode: [], // 选取的节点下标组
            index: -1,
            point: -1 // 选取的点数的下标
        }
    }
}

useEventListener('keydown', keyMap)

defineExpose({
    addNode
})
</script>

<style>
.svgEach {
    position: relative;
}

.connector {
    stroke: hsla(0, 0%, 50%, 0.6);
    stroke-width: 2px;
    fill: none;
}

.simulate_sel_area {
    border: 3px dashed blue;
    position: absolute;
}

.menu_contain {
    width: 100%;
    border: 1px solid rgba(1, 1, 1, 0.3);
    background: #ffffff;
    border-radius: 5px;
    font-size: 16px;
    display: inline-grid;
}

.menu_contain span {
    display: inline-block;
    display: -moz-inline-box;
    width: calc(100% - 20px);
    cursor: pointer;
    padding: 5px 10px;
}

.menu_contain span:hover {
    background-color: rgba(40, 157, 233, 0.3);
    cursor: pointer;
}
</style>
